Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(localchain): add transfer method to LocalChainAccountKit holder #9380

Merged
merged 4 commits into from
May 24, 2024

Conversation

0xpatrickdev
Copy link
Member

refs: #9193

Description

Adds .transfer() method to LocalChainAccountKit, working towards OrchestrationAccountI['transfer'] interface.

Security Considerations

Scaling Considerations

Documentation Considerations

Testing Considerations

Upgrade Considerations

Copy link

cloudflare-workers-and-pages bot commented May 17, 2024

Deploying agoric-sdk with  Cloudflare Pages  Cloudflare Pages

Latest commit: fd11145
Status: ✅  Deploy successful!
Preview URL: https://ec03c886.agoric-sdk.pages.dev
Branch Preview URL: https://9193-ibc-transfer-from-lca.agoric-sdk.pages.dev

View logs

@0xpatrickdev 0xpatrickdev force-pushed the 9193-withdraw-from-lca branch 3 times, most recently from b9e751c to da20cb6 Compare May 22, 2024 15:26
Base automatically changed from 9193-withdraw-from-lca to master May 22, 2024 15:51
@0xpatrickdev 0xpatrickdev marked this pull request as ready for review May 22, 2024 18:51
@@ -1,11 +1,30 @@
// @ts-checck
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We might consider renaming this - it's meant to capture the OrchestrationAccount interface implemented for a LocalChainAccount.


// TODO #8879 chainInfo and #9063 well-known chains
const { transferChannel } =
this.state.agoricChainInfo.connections.get(destination.chainId);
Copy link
Member Author

@0xpatrickdev 0xpatrickdev May 22, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The destination: ChainAddress argument seems to assume we should be able to get channelIds from a chainId

}),
'can create transfer msg with memo',
);
// TODO, can we intercept the bridge message to see that it has a memo?
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cc @turadg

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah. I could add that to #9396 but I wouldn't have a case to drive it. I'd prefer to sequence that first and add the functionality here. Consider rebasing this branch on top of 9207-bridge

@0xpatrickdev 0xpatrickdev requested review from turadg and dckc May 22, 2024 19:14
agoricChainInfo,
});
}

const publicFacet = zone.exo('StakeBld', undefined, {
makeStakeBldInvitation() {
return zcf.makeInvitation(
async seat => {
const { give } = seat.getProposal();
trace('makeStakeBldInvitation', give);
// XXX type appears local but it's remote
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this comment was for E(privateArgs.localchain).makeAccount(). best to remove it at this point

packages/orchestration/src/examples/stakeBld.contract.js Outdated Show resolved Hide resolved
Comment on lines 110 to 111
undefined,
undefined,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

omission has the same effect

Suggested change
undefined,
undefined,

Comment on lines 90 to 92
* @param {TimerService} initState.timer
* @param {TimerBrand} initState.timerBrand
* @param {AgoricChainInfo} initState.agoricChainInfo
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

these aren't specific to each account kit, right? they can stay in the prepareLocalChainAccountKit closure.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good call out, thank you

packages/orchestration/src/exos/local-chain-account-kit.js Outdated Show resolved Hide resolved
memo: opts?.memo ?? '',
}),
]);
void trace('MsgTransfer result', result);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

trace is sync.

Suggested change
void trace('MsgTransfer result', result);
trace('MsgTransfer result', result);

* @returns void
*
* TODO document the mapping from the address to the destination chain.
*/
transfer: (
amount: AmountArg,
destination: ChainAddress,
memo?: string,
opts?: IBCMsgTransferOptions,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@dtribble wants to review changes to orchestration-api. These look to me like fixups of the existing API so I wouldn't block awaitinghis review

type StartFn =
typeof import('@agoric/orchestration/src/examples/stakeBld.contract.js').start;

test('stakeBld contract - makeAccount, deposit, withdraw', async t => {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thanks I missed this one when adding tests of the examples

}),
'can create transfer msg with memo',
);
// TODO, can we intercept the bridge message to see that it has a memo?
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah. I could add that to #9396 but I wouldn't have a case to drive it. I'd prefer to sequence that first and add the functionality here. Consider rebasing this branch on top of 9207-bridge

/**
* make a new durable baggage and zone for each vat
*/
export const makeBaggageAndZone = () => {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

odd to have both. consider makeRootZone and make baggage from it as needed

rootZone.mapStore('publisher')

Copy link
Member

@dckc dckc left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sharing thoughts so far, before going to a meeting

versions: { identifier: string; features: string[] }[];
delay_period: bigint;
};
connections: MapStore<string, IBCConnectionInfo>; // chainId or wellKnownName
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

mutable... hm... 🤔

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What would you expect to see instead?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure... it's an adjustment to my mental model, and I'm not sure where I might have assumed that ChainInfo is immutable. I just need to be aware of it going forward, I suppose.

(technically, this still is immutable; the value of the connections property doesn't change. it's always the same MapStore. It's just the contents of that MapStore that may change.)

One impact is the contents of connections aren't included when a CosmosChainInfo is marshaled to vstorage as part of agoricNames.

Comment on lines 37 to 38
const timerService = await timerServiceP;
const timerBrand = await E(timerService).getTimerBrand();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

there's an unnecessary round-trip here. I wonder whether it's worth using Promise.all() to remove it.

@@ -37,6 +46,8 @@ export const startStakeBld = async ({
terms: {},
privateArgs: {
localchain: await localchain,
timerBrand,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems a little odd to pass timerBrand in separately. The contract has to rely on the caller to make sure it corresponds to timerService. Well, I suppose contracts do rely on their caller in general. So it's not a serious problem. It just feels weird.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was looking to cache the brand since it's a remote call. I can do this in the start function instead.

* @param {RelativeTimeRecord} [relativeTime] defaults to 5 minutes
* @returns {Promise<bigint>} Timeout timestamp in absolute nanoseconds since unix epoch
*/
async getCurrentTimestamp(relativeTime) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's not use the same name (getCurrentTimestamp) for a different behavior.

Perhaps getTimeoutNS()?

*/
async getCurrentTimestamp(relativeTime) {
const currentTime = await E(timer).getCurrentTimestamp();
return (
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm a little surprised the linter didn't remove the outer parens.

@@ -29,25 +32,34 @@ export type CosmosValidatorAddress = ChainAddress & {
addressEncoding: 'bech32';
};

/** Info for an IBC Connection (Chain:Chain relationship, that can contain multiple Channels) */
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/** Info for an IBC Connection (Chain:Chain relationship, that can contain multiple Channels) */
/** Info for an IBC Connection (Chain:Chain relationship that can contain multiple Channels) */

A restrictive clause is not offset with commas. -- Restrictive Clause: Explanation and Examples

or change "that" to "which" for a non-restrictive clause:

Suggested change
/** Info for an IBC Connection (Chain:Chain relationship, that can contain multiple Channels) */
/** Info for an IBC Connection (Chain:Chain relationship, which can contain multiple Channels) */

I'm cursed with a brain that throws syntax errors on English grammar just as much as programming language grammar. :-/

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you - I appreciate your appreciation of grammar!

export type IBCConnectionInfo = {
id: string; // e.g. connection-0
client_id: string; // '07-tendermint-0'
state: 'OPEN' | 'TRYOPEN' | 'INIT' | 'CLOSED';
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I noticed that on-the-wire it's STATE_OPEN, not OPEN. Did we diverge on purpose?
orthogonal to this PR

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oversight, these should all be prefixed with STATE_, and CLOSED doesn't exist. But yes orthogonal.

Also seeing a StateSDKType type in @agoric/cosmic-proto/ibc/core/connection/v1/connection.js we can use instead. (And a similar one in /ibc/core/channel/v1/channel.js)

versions: { identifier: string; features: string[] }[];
delay_period: bigint;
};
connections: MapStore<string, IBCConnectionInfo>; // chainId or wellKnownName
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

when do we use chainId vs wellKnownName?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good question, I am also wondering the same. The transfer parameters imply we should be able to get there from chainId or chainId -> wellKnownName. https://github.com/Agoric/agoric-sdk/pull/9380/files#r1610510589

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think solving this can wait for #9063

* @param {import('@agoric/vats/src/localchain.js').LocalChainAccount} account
* @param {StorageNode} storageNode
* @param {object} initState
* @param {LocalChainAccount} initState.account
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you need a LocalChainAccount to make a LocalchainAccountKit? That hurts my brain. Usually, we have either makeX or makeXKit, not both.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agree this doesn't make sense. I suggested we rename this to something closer to OrchestrationAccountKit here:
https://github.com/Agoric/agoric-sdk/pull/9380/files#r1610504823

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

that makes some sense. Though will OrchestrationAccountKit be the thing for all OrchestratrionAccounts?

Another option is LocalChainAccountHolder, since we will be wrapping the LCA object in an Ownable holder for rights transfer. #9087

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Though will OrchestrationAccountKit be the thing for all OrchestratrionAccounts

I'm not sure the best approach here yet, I suppose there are multiple.

I did not end up tackling this change here -seems like it can be picked up as part of #9212

Comment on lines 129 to 168
const result = await E(lca).executeTx([
const [result] = await E(lca).executeTx([
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

wild... was there a bug here?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The expected return for delegate() is Promise<void>, so no. In the next pass I can change the return statement of this function.

versions: { identifier: string; features: string[] }[];
delay_period: bigint;
};
connections: MapStore<string, IBCConnectionInfo>; // chainId or wellKnownName
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think solving this can wait for #9063

@@ -1,6 +1,8 @@
/**
* @file Example contract that uses orchestration
*/
// @ts-check
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the tsconfig already has checkJs: true. Please keep @ts-check out of this package so it doesn't look like it's necessary.

Comment on lines 48 to 52
const timerBrand = await E(privateArgs.timerService).getTimerBrand();
const timestampHelper = makeTimestampHelper(
privateArgs.timerService,
timerBrand,
);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't see timerBrand used here. Consider,

Suggested change
const timerBrand = await E(privateArgs.timerService).getTimerBrand();
const timestampHelper = makeTimestampHelper(
privateArgs.timerService,
timerBrand,
);
const timestampHelper = await makeTimestampHelper(
privateArgs.timerService
);

Or if you expect to need timeBrand in the future,

Suggested change
const timerBrand = await E(privateArgs.timerService).getTimerBrand();
const timestampHelper = makeTimestampHelper(
privateArgs.timerService,
timerBrand,
);
const { timerBrand, timestampHelper } = await makeTimestampHelper(
privateArgs.timerService
);

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't see timerBrand used here.

It's used in makeTimestampHelper on line 31 to build the default, right?

https://github.com/Agoric/agoric-sdk/pull/9380/files#diff-31a54bae8709455401dcdf383a90d4eddb79f4916433172da12e13075858e71cR31

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

makeTimestampHelper, taking the timerService, can get the brand itself. That's why my examples change it to async

Comment on lines 76 to 77
makeAcountInvitationMaker: M.call().returns(M.promise()),
makeStakeBldInvitation: M.call().returns(M.promise()),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this way it'll validate the return value. It at least improves the documentation. We should use this style so when we start inferring TS types from the Pattern the info is there.

Suggested change
makeAcountInvitationMaker: M.call().returns(M.promise()),
makeStakeBldInvitation: M.call().returns(M.promise()),
makeAcountInvitationMaker: M.callWhen().returns(InvitationShape),
makeStakeBldInvitation: M.callWhen().returns(InvitationShape),

Comment on lines 86 to 94
const lcaSeatKit = zcf.makeEmptySeatKit();
atomicTransfer(zcf, seat, lcaSeatKit.zcfSeat, give);
seat.exit();
trace('makeStakeBldInvitation tryExit lca userSeat');
await E(lcaSeatKit.userSeat).tryExit();
trace('awaiting payouts');
const payouts = await E(lcaSeatKit.userSeat).getPayouts();
trace('awaiting deposit');
await E(holder).deposit(await payouts.In);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh it's in master now

// deposit funds from user seat to LocalChainAccount
const payments = await withdrawFromSeat(zcf, seat, give);
await deeplyFulfilled(objectMap(payments, localAccount.deposit));

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unrelated to changes in this PR but happy to include

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, these lines are + but they were moved. Please if you don't mind

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I went with a sightly different approach:

const { In } = await deeplyFulfilled(
  withdrawFromSeat(zcf, seat, give),
);
await E(holder).deposit(In);

Saw this with objectMap:

ℹ RemoteError(error:captp:far-zoeTest#20001)#5: Method "In \"deposit\" method of (Account Holder holder)" called without 'this' object
ℹ Error: Method "In \"deposit\" method of (Account Holder holder)" called without 'this' object

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if that's the one payment then that works. The other is more general and I suspect we'll want a utility for it. Something to look into near the end as we clean up the DX

* @param {import('@agoric/vats/src/localchain.js').LocalChainAccount} account
* @param {StorageNode} storageNode
* @param {object} initState
* @param {LocalChainAccount} initState.account
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

that makes some sense. Though will OrchestrationAccountKit be the thing for all OrchestratrionAccounts?

Another option is LocalChainAccountHolder, since we will be wrapping the LCA object in an Ownable holder for rights transfer. #9087

@@ -145,17 +145,17 @@ export interface OrchestrationAccountI {
/**
* Transfer an amount to another account, typically on another chain.
* The promise settles when the transfer is complete.
* @param amount - the amount to transfer.
* @param amount - the amount to transfer. Can be provided as pure data using denoms or as native Amounts.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"native" is ambiguous as to whether native to Agoric or the host chain.

Suggested change
* @param amount - the amount to transfer. Can be provided as pure data using denoms or as native Amounts.
* @param amount - the amount to transfer. Can be provided as pure data using denoms or as ERTP Amounts.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍 updating the AmountArg annotation as well


t.is(
await getCurrentTimestamp(
TimeMath.coerceRelativeTimeRecord(1n, timerBrand),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

isn't this more general? why should time export the 'make' function?

'simulated unexpected MsgTransfer packet timeout',
);
}
// TODO, can we reference bank to ensure user has tokens they are transfering?
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fee free to mention #9402

@@ -99,7 +101,8 @@ export const start = async (zcf, privateArgs, baggage) => {
{
async makeAccount() {
trace('makeAccount');
return makeAccount().then(({ account }) => account);
const { account } = await makeAccount();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if account is a property of the return value rather than the return value itself, the function should be called makeAccountKit, not makeAccount.

baggage,
makeRecorderKit,
zcf,
timestampHelper,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

that seems like an odd factoring. Is prepareLocalChainAccountKit supposed to work with an arbitrary implementation of this interface? Doesn't seem like it. Passing the timerService seems more straightforward.

Pass timerBrand too if awaiting it inside prepareLocalChainAccountKit would be a problem.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agree the caller of prepareLocalChainAccountKit should only have to know about timerService. See 68c8762. I also moved getTimerBrand() back to the proposal to avoid an await during prepare.

);
trace('awaiting deposit');
await E(account).deposit(await payouts.In);
async function makeLocalAccount() {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

as noted earlier:

Suggested change
async function makeLocalAccount() {
async function makeLocalAccountKit() {

cf docs on Method Naming Structure

sourceChannel: transferChannel.channelId,
token: {
amount: String(amount.value),
// @ts-expect-error AmountArg already narrowed to DenomAmount
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if you change the !matches(...) || Fail... to if (typeof amount !== 'string') throw ..., I think you won't need this override.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if ('brand' in amount) makes it happy. in, more precisely

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I frequently forget to do this instead of if (amount.brand). Hopefully after writing it out now, I won't 🙂

Comment on lines 205 to 206
// TODO #9211 lookup denom from brand
!matches(amount, AmountShape) || Fail`ERTP Amounts not yet supported`;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure what #9211 should add here. Looking up a denom for a brand in agoricNames.vbankAsset is straightforward.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fair, really only comes into play for ICA accounts. Will still tackle this in a follow up.

@@ -0,0 +1,87 @@
/**
* @file Mocked Chain Info object until #8879
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

UNTIL is a handy notation, but I'm uneasy that there's little to stop us from closing issues without addressing them.

maybe a back-link to this PR will help a little:

* currently keyed by ChainId, as this is what we have
* available in ChainAddress to determine the correct IBCChannelID's
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

I'm more comfortable relying on chainid since learning that they're available in-protocol (client state).

I don't think the protocol guarantees uniqueness across the interchain, so when we query for client state, we'll have to be careful to ... um... do something if we find a collision.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok cool. The only potential hiccup I see, if human readability of the identifier is a design constraint, is that evm chain ids are typically integers.

ordering: Order.ORDER_UNORDERED,
state: IBCChannelState.STATE_OPEN,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if using these symoblic constants rather than literals was due to typing, you can address that by surrounding the whole object literal with a const type cast.

* @param {Zone} zone
* @returns {Pick<CosmosChainInfo, 'connections' | 'chainId'>}
*/
export const prepareMockChainInfo = zone => {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems like odd factoring. Passing the mapstore seems more straightforward. Then you can call it addAllChainInfo or the like.

await t.throwsAsync(
() => E(account).transfer({ denom: 'ubld', value: 1n }, unknownDestination),
{
message: /not found/,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

test that the bad chainId (unknown) is included in the message?

suggest: choose something more novel than unknown as the bad value

E(account).transfer({ denom: 'ubld', value: 10n }, destination, {
memo: 'hello',
}),
'can create transfer msg with memo',
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder about unit tests for just building the msg. you had a file of message builders in cosgov. that might be handy

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Worth exploring. With jest I would reach for something like toHaveBeenCalledWith for on toBridge.

Copy link
Member

@turadg turadg May 24, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You could use sinon to spy, but be careful with shared state

And it doesn't play well with SES and durability. When we need such capability we've been making fakes and putting in that instrumentation.

Copy link
Member

@dckc dckc left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I still have lots of suggestions to consider, but I suppose none of them is critical.

The implementation of the feature looks correct and the tests are sufficient.


// XXX is this safe to call before prepare statements are completed?
Copy link
Member

@turadg turadg May 23, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nope, it's a remote call and those can't happen in the first crank of upgrade. but we can tackle that when this gets an upgrade test

@0xpatrickdev 0xpatrickdev added the automerge:rebase Automatically rebase updates, then merge label May 24, 2024
- towards `OrchestrationAccount` interface for a `LocalChainAccount`
- includes `mockChainInfo` as IBCChannelIDs are needed to construct a transfer message from the `ChainAddress` parameter
@mergify mergify bot merged commit 96f4c73 into master May 24, 2024
63 checks passed
@mergify mergify bot deleted the 9193-ibc-transfer-from-lca branch May 24, 2024 03:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
automerge:rebase Automatically rebase updates, then merge
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants